package com.kaiedu.spring.ioc;

import com.kaiedu.spring.annotation.Scope;
import com.kaiedu.spring.annotation.kaiAutowired;
import com.kaiedu.spring.annotation.kaiComponent;
import com.kaiedu.spring.annotation.kaiComponentScan;
import com.kaiedu.spring.processor.initializingBean;
import com.kaiedu.spring.processor.kaiBeanPostProcessor;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author kai
 * @version 1.0
 * @date 2024/6/11
 * @Description
 */
@SuppressWarnings("all")
public class kaiSpringApplicationContext {
    private Class configClass;
    //ioc是我存放的通过反射创建的对象（基于注解方式）

    //定义属性
    //将 bean 的定义，放在这个 beanDefinitionMap 集合
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //如果 bean 是单例的，就直接放在这个 单例 bean 对象池
    private ConcurrentHashMap<String, Object> singletonMap = new ConcurrentHashMap<>();

    //    定义一个属性ArrayList，存放后置处理器
    private List<kaiBeanPostProcessor> beanPostProcessorslist = new ArrayList<>();

    //构造器
    public kaiSpringApplicationContext(Class configClass) {
        BeanDefinitionsScan(configClass);

        //通过 beanDefinitionMap , 初始化 singletonObjects bean 单列池
        Enumeration<String> keys = beanDefinitionMap.keys();
        while (keys.hasMoreElements()) {

            //得到beandefinatiom中的bean  key-value 从而得到value
            String beanName = keys.nextElement();

            //通过beanName得到map中对应的对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //将该实例放入到singleton集合中
                Object bean = createBean(beanName, beanDefinition);
                singletonMap.put(beanName, bean);
            }

        }


        //System.out.println("beanDefinitionMap=" + beanDefinitionMap);
        //System.out.println("单例池=" + singletonMap);
    }

    //完成对指定包的扫描
    public void BeanDefinitionsScan(Class configClass) {
        this.configClass = configClass;
        //System.out.println("this.configClass=" +this.configClass);
        //获取要扫描的包
        //1.先得到kaispringConfig配置的@kaiComponentScan
        kaiComponentScan kaiComponentScan1 = (kaiComponentScan) this.configClass.getDeclaredAnnotation(kaiComponentScan.class);

        //2.通过componentScan的value =>即要扫描的包
        String path = kaiComponentScan1.value();
        //System.out.println("扫描的包" + path);

        //得到扫描的包下的所有资源(类.class)
        // 1.得到类的加载器
        ClassLoader classLoader =
                kaiSpringApplicationContext.class.getClassLoader();
        //通过类的加载器获取到要扫描的包的资源url
        URL resource = classLoader.getResource("com/kaiedu/spring/component");
        //System.out.println("resource"+resource);
        //3.将要加载的资源进行遍历
        File file = new File(resource.getFile());
        //System.out.println(file);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
               /* System.out.println("==========");
                System.out.println(f.getAbsolutePath());*/
                String absolutePath = f.getAbsolutePath();

                //只处理.class文件
                if (absolutePath.endsWith(".class")) {

                    String classname =
                            absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf(".class"));
                    //获取类的全路径
                    String classForName = path + "." + classname;
                    // System.out.println(classForName);
                    //判断是否需要注入容器(有的不是bean，普通类无需注入)
                    try {
                        Class<?> clazz = classLoader.loadClass(classForName);

                        if (clazz.isAnnotationPresent(kaiComponent.class)) {

                            //clazz.getDeclaredAnnotation()

                            BeanDefinition beanDefinition = new BeanDefinition();

                            beanDefinition.setClazz(clazz);


                            //配置后置处理器
                            if (kaiBeanPostProcessor.class.isAssignableFrom(clazz)) {

                                kaiBeanPostProcessor o = (kaiBeanPostProcessor) clazz.newInstance();

                                beanPostProcessorslist.add(o);
                                continue;
                            }


                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope declaredAnnotationscope = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(declaredAnnotationscope.value());

                            } else {

                                beanDefinition.setScope("singleton");
                            }

                            kaiComponent component = clazz.getDeclaredAnnotation(kaiComponent.class);
                            String id = component.value();


                            if (!id.isEmpty()) {
                                //
                                classname = id;
                            } else {

                                char firstChar = classname.charAt(0);
                                char lowerFirstChar = Character.toLowerCase(firstChar);
                                classname = lowerFirstChar + classname.substring(1);
                            }

                            beanDefinitionMap.put(classname, beanDefinition);
                            //System.out.println("是一个 bean = " + clazz + "   =" + classname);

                         /*   //这时就可以反射对象，放入到容器中
                            Class<?> classzz = Class.forName(classForName);
                            Object newInstance = classzz.newInstance();

                            //放入到容器
                            ioc.put(StringUtils.uncapitalize(classname), newInstance);*/

                        } else {
                            //System.out.println("不是bean======" + clazz);

                        }

                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }


        }
    }


    //实例化bean
    private Object createBean(String beanname, BeanDefinition beanDefinition) {

        Class clazz = beanDefinition.getClazz();

        try {
            //通过反射获得实例
            Object instance = clazz.getDeclaredConstructor().newInstance();

            //加入依赖注入的业务逻辑

            //1.遍历当前要创建的对象的所有字段
            for (Field declarefield : clazz.getDeclaredFields()) {

                //isAnnotationPresent()用于检查某个类、方法、字段或其他程序元素是否被指定的注解（Annotation）标注
                if (declarefield.isAnnotationPresent(kaiAutowired.class)) {
                    // 获取字段名
                    String name = declarefield.getName();

                    // 根据字段名获取对应的依赖对象
                    Object getbean1 = getbean(name);

                    // 设置字段为可访问（即使是私有字段）
                    declarefield.setAccessible(true);

                    // 将依赖对象注入到当前对象的对应字段中
                    declarefield.set(instance, getbean1);

                }


            }

            System.out.println("====创建好bean====" + instance);

            for (kaiBeanPostProcessor kaibeanpostprocessor : beanPostProcessorslist) {
                Object cueernt = kaibeanpostprocessor.postProcessBeforeInitialization(instance, beanname);
                if (cueernt != null) {
                    instance = cueernt;
                }
            }
            /**
             *  如果 instance 实现了 InitializingBean 接口，那么调用它的 afterPropertiesSet() 方法。
             *  这个方法是 InitializingBean 接口的一个抽象方法，用于在 bean 的属性设置完成后执行一些自定义的初始化操作。
             */
            if (instance instanceof initializingBean) {
                ((initializingBean) instance).afterPropertiesSet();


            }

            /*后置*/
            for (kaiBeanPostProcessor kaibeanpostprocessor : beanPostProcessorslist) {
                Object cueernt  = kaibeanpostprocessor.postProcessAfterInitialization(instance, beanname);

                if (cueernt != null) {
                    instance = cueernt;
                }
            }

            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;

    }

    //编写方法getbean（String name），编写方法返回容器中对象
    public Object getbean(String name) {

        if (beanDefinitionMap.containsKey(name)) {


            BeanDefinition beanDefinition = beanDefinitionMap.get(name);

            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {

                return singletonMap.get(name);

            } else {
                return createBean(name, beanDefinition);
            }
        } else {
            throw new NullPointerException("没有该bean");
        }


    }

}
